package logs

import (
	"sync"
	"fmt"
	"runtime"
	"path"
	"os"
	"time"
)

const (
	DebugLevel = iota
	InfoLevel
	WarnLevel
	ErrorLevel
	FatalLevel
	PanicLevel
)

/**
输出方式接口
@func Set() 设置输出方式的基础配置
@func Write() 输出消息
@func Close() 关闭输出方式
 */
type Output interface {
	Set(config []byte) error
	Write(when time.Time, msg string, level int) error
	Flush()
	Close()
}

/**
输出方式生成器
输出方式注册New方法到适配器中,通过适配器调用所有实现Output接口的输出方式
 */
type OutputCreater func() Output

//适配器
var adapters = make(map[string]OutputCreater)


/**
注册输出方法
如果输出方法注册两次或者为空,则panic错误
 */
func Register(name string, outputCreater OutputCreater)  {
	if outputCreater == nil {
		panic("log: Register log OutputCreater is nil!")
	}
	if _,ok := adapters[name]; ok {
		panic("Log: Register call name twice for "+ name)
	}

	adapters[name] = outputCreater
}


/**
日志类
@attr lock sync.Mutex 锁
@attr level int 日志级别
@attr flag bool 是否打印调用的那个函数的行和文件
@attr callDepth int 访问的深度,默认为2
@attr async bool 异步发送消息

@attr msg chan  消息管道
@attr outputs map[string]Output 输出类型数组,文件输出,命令行输出...


callDepth = 0 为当前文件的 main.main 函数,
callDepth = 1 runtime.main
callDepth = 2 runtime.goexit.为真正的函数入口

 */
type Logger struct  {
	lock sync.Mutex
	level int
	flag bool
	callDepth int
	async bool
	msgChan chan *logMsg
	outputs map[string]Output
}

/**
日志消息类
@attr level int 消息级别
@attr msg string 消息
 */
type logMsg struct {
	level int
	msg string
	when time.Time
}


var logMsgPool *sync.Pool

/**
创建日志
@param length int64 消息长度
@return *Logger 返回Logger的指针
 */
func NewLogger(length int64) *Logger {
	log := new(Logger)
	log.level = DebugLevel
	log.callDepth = 2
	log.msgChan = make(chan *logMsg, length)
	log.outputs = make(map[string]Output)
	return log
}


/**
异步发送消息
@return *Logger
 */
func (l *Logger) Async() *Logger{
	l.async = true
	logMsgPool = &sync.Pool{
		New:func() interface{} {
			return &logMsg{}
		},
	}
	go l.start()
	return l
}

/**
日志设置输出方式
@param name string 输出方式名
@param config string json字符串格式配置文件
@return error|nil
 */
func (l *Logger) SetOutput(name string, config []byte) error {
	l.lock.Lock()
	defer l.lock.Unlock()

	if outputCreater,ok := adapters[name]; ok {
		out := outputCreater()
		err := out.Set(config)
		if err != nil {
			fmt.Println("log: Logger.Set(): " + err.Error())
			return err
		}
		l.outputs[name] = out
	}else {
		return fmt.Errorf("log: unknown name %q (forget register?)!",name)
	}
	return nil
}

/**
日志删除输出方式
@param name string 输出方式名
@return error|nil
 */
func (l *Logger) DeleteOutput(name string) error {
	l.lock.Lock()
	defer l.lock.Unlock()

	if out,ok := l.outputs[name]; ok {
		out.Close()
		delete(l.outputs, name)
	}else {
		return fmt.Errorf("log: unknown name %q (forgotten Register?)", name)
	}

	return nil
}

/**
日志输出消息
@param level int 级别
@param msg string 消息
@return error | nil
 */
func (l *Logger) Write(msg string, level int) error  {
	when := time.Now() // get this early.
	if l.flag {
		_,file, line, ok := runtime.Caller(l.callDepth)
		if !ok {
			file = "???"
			line = 0
		}
		_,filename := path.Split(file)
		msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)
	}else {
		//msg = msg  //else 无用
	}

	if l.async { //异步
		logMsg := logMsgPool.Get().(*logMsg)
		logMsg.level = level
		logMsg.msg = msg
		logMsg.when = when
		l.msgChan<-logMsg

	}else {
		l.writeLogger(when, msg, level)
	}


	return nil
}
/**
根据输出方式输出信息
 */
func (l *Logger) writeLogger(when time.Time, msg string, level int) {
	for name,out := range l.outputs  {
		if err := out.Write(when, msg, level); err != nil {
			fmt.Fprintf(os.Stderr, "unable to write to adapter:%v, error:%v\n", name, err)
		}
	}
}


/**
日志设置级别
@param level int 级别
 */
func (l *Logger) SetLevel(level int)  {
	l.level = level
}

//设置是否使用栈信息
func (l *Logger) SetFlag(b bool)  {
	l.flag = b
}

//设置访问栈深度
func (l *Logger) SetCallDepth(d int)  {
	l.callDepth = d
}

//获取访问栈的深度
func (l *Logger) GetCallDepth() int  {
	return l.callDepth
}



/**
日志启动
开始监听并循环讲注册进来的各种日志方式进行输出
 */
func (l *Logger) start()  {
	for  {
		select {
		case logMsg := <-l.msgChan:
			l.writeLogger(logMsg.when, logMsg.msg, logMsg.level)
			logMsgPool.Put(logMsg)
		}
	}
}


func (l *Logger) Debug(format string, v ...interface{}) {
	if l.level > DebugLevel {
		return
	}
	msg := fmt.Sprintf("log[D]:"+format, v ...)
	l.Write(msg, DebugLevel)
}


func (l *Logger) Info(format string, v ...interface{}) {
	if l.level > InfoLevel {
		return
	}
	msg := fmt.Sprintf("log[I]:"+format, v ...)
	l.Write(msg, InfoLevel)
}


func (l *Logger) Warn(format string, v ...interface{}) {
	if l.level > WarnLevel {
		return
	}
	msg := fmt.Sprintf("log[W]:"+format, v ...)
	l.Write(msg, WarnLevel)
}

func (l *Logger) Error(format string, v ...interface{}) {
	if l.level > ErrorLevel {
		return
	}
	msg := fmt.Sprintf("log[E]:"+format, v ...)
	l.Write(msg, ErrorLevel)
}

func (l *Logger) Fatal(format string, v ...interface{}) {
	if l.level > FatalLevel {
		return
	}
	msg := fmt.Sprintf("log[F]:"+format, v ...)
	l.Write(msg, FatalLevel)
}

func (l *Logger) Panic(format string, v ...interface{}) {
	if l.level > PanicLevel {
		return
	}
	msg := fmt.Sprintf("log[P]:"+format, v ...)
	l.Write(msg, PanicLevel)
}

// Printf calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Printf(format string, v ...interface{}) {
	l.Write(fmt.Sprintf(format, v...), 1)
}

// Print calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Print(v ...interface{}) { l.Write(fmt.Sprint(v...), 1) }

// Println calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Println.
func (l *Logger) Println(v ...interface{}) { l.Write(fmt.Sprintln(v...), 1) }

/**
输出缓存
 */
func (l *Logger) Flush()  {
	for _,out := range l.outputs  {
		out.Flush()
	}
}


/**
日志关闭
将所有输出方式都关闭
 */
func (l *Logger) Close()  {
	for ;len(l.msgChan) > 0;  {
		logMsg := <-l.msgChan
		l.writeLogger(logMsg.when, logMsg.msg, logMsg.level)
		logMsgPool.Put(logMsg)
	}
	for _,out := range l.outputs  {
		//out.Flush()
		out.Close()
	}
	l.outputs = nil
}


func formatTimeHeader(when time.Time) ([]byte) {
	year, month, day := when.Date()
	hour, min, sec := when.Clock()
	//len(2006/01/02 15:03:04)==19
	var buf [20]byte
	t := 3
	for year >= 10 {
		p := year / 10
		buf[t] = byte('0' + year - p*10)
		year = p
		t--
	}
	buf[0] = byte('0' + year)
	buf[4] = '/'
	if month > 9 {
		buf[5] = '1'
		buf[6] = byte('0' + month - 10)
	} else {
		buf[5] = '0'
		buf[6] = byte('0' + month)
	}
	buf[7] = '/'
	t = day / 10
	buf[8] = byte('0' + t)
	buf[9] = byte('0' + day - t*10)
	buf[10] = ' '
	t = hour / 10
	buf[11] = byte('0' + t)
	buf[12] = byte('0' + hour - t*10)
	buf[13] = ':'
	t = min / 10
	buf[14] = byte('0' + t)
	buf[15] = byte('0' + min - t*10)
	buf[16] = ':'
	t = sec / 10
	buf[17] = byte('0' + t)
	buf[18] = byte('0' + sec - t*10)
	buf[19] = ' '

	return buf[0:]
}